package com.shark.uh.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.shark.uh.enums.ResponseCode;
import com.shark.uh.enums.UserStatus;
import com.shark.uh.exception.BusinessException;
import com.shark.uh.mapper.UserMapper;
import com.shark.uh.model.dto.auth.LoginCheckDTO;
import com.shark.uh.model.dto.auth.LoginCheckResultDTO;
import com.shark.uh.model.dto.auth.LoginDTO;
import com.shark.uh.model.dto.auth.RegisterDTO;
import com.shark.uh.model.entity.User;
import com.shark.uh.service.*;
import com.shark.uh.utils.PasswordEncryptor;
import com.shark.uh.utils.SnowflakeIDGenerator;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

@Service
@Log4j2
public class AuthServiceImpl implements AuthService {
    @Resource
    private ImageCaptchaService imageCaptchaService;

    @Resource
    private UserService userService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private JavaMailSender mailSender;

    @Resource
    private MailProperties mailProperties;

    @Resource
    private RedisService redisService;

    @Value("${app.base-url}")
    private String baseUrl;

    @Value("${app.login-url}")
    private String loginUrl;

    @Value("${app.index-url}")
    private String indexUrl;

    @Value("${app.login-check-url}")
    private String loginCheckUrl;

    private final String SUB_SYSTEM_LOGIN_URL = "subsystemLoginUrl";

    private final String SUB_SYSTEM_BACK_URL= "back";


    /**
     * 注册
     *
     * @param registerDTO 注册数据传输对象
     */
    @Override
    public void register(RegisterDTO registerDTO) {
        // 图形验证码校验
        imageCaptchaService.verify(registerDTO.getCodeId(), registerDTO.getCode());


        // 密码相同性校验
        String password = registerDTO.getPassword();
        String rePassword = registerDTO.getRePassword();
        if (!password.equals(rePassword)) {
            throw new BusinessException(ResponseCode.REPASS_WRONG);
        }

        // 邮箱是否被注册
        if (userMapper.countByEmail(registerDTO.getEmail()) > 0) {
            throw new BusinessException(ResponseCode.USER_ALREADY_EXISTS);
        }

        // 密码加密
        String salt = PasswordEncryptor.generateSalt();
        String hashPassword = PasswordEncryptor.hashPassword(registerDTO.getPassword(), salt);

        // 生成初始username
        SnowflakeIDGenerator snowflakeIDGenerator = new SnowflakeIDGenerator(1);
        String username = "shark_" + snowflakeIDGenerator.generateId();

        // 存入数据库
        User user = new User();
        user.setUsername(username);
        user.setEmail(registerDTO.getEmail());
        user.setPassword(hashPassword);
        user.setSalt(salt);
        userService.save(user);

        // 发送邮件
        String unionToken = String.valueOf(snowflakeIDGenerator.generateId());
        redisService.saveToRedisMinute(unionToken, registerDTO.getEmail(), 10);
        String activeUrl = baseUrl + "/view/user/active/" + unionToken;
        SimpleMailMessage message = new SimpleMailMessage();

        message.setFrom(mailProperties.getUsername());
        message.setTo(registerDTO.getEmail());
        message.setSubject("激活账号");
        message.setText(activeUrl);
        mailSender.send(message);
    }


    /**
     * 激活账号
     *
     * @param token 唯一值
     * @return 激活数量
     */
    @Override
    public int activeAccount(String token) {
        String email = redisService.getString(token);
        redisService.del(token);
        return userMapper.setStatus(email, UserStatus.ACTIVE.getStatus());
    }

    /**
     * 登陆服务
     *
     * @param loginDTO 登录信息
     */
    @Override
    public Object login(LoginDTO loginDTO) throws UnsupportedEncodingException {
        // 查询数据库
        User user = userMapper.getUserByEmail(loginDTO.getEmail());

        // 查询不到
        if (user == null) {
            throw new BusinessException(ResponseCode.USER_NOT_FOUND);
        }

        // 验证密码
        String hashedPassword = PasswordEncryptor.hashPassword(loginDTO.getPassword(), user.getSalt());
        if (!hashedPassword.equals(user.getPassword())) {
            throw new BusinessException(ResponseCode.PASSWORD_WRONG);
        }

        StpUtil.login(user.getId());

        // 构造请求url
        String userHubLoginCheckUrl = UriComponentsBuilder.fromHttpUrl(loginCheckUrl)
                .queryParam(SUB_SYSTEM_LOGIN_URL, URLEncoder.encode(loginDTO.getSubsystemLoginUrl(), StandardCharsets.UTF_8.name()))
                .queryParam(SUB_SYSTEM_BACK_URL, URLEncoder.encode(loginDTO.getBack(), StandardCharsets.UTF_8.name()))
                .build().toUriString();

        // 将数据存入HashMap
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("tokenInfo", StpUtil.getTokenValue());
        hashMap.put("redirectUrl", userHubLoginCheckUrl);

        return hashMap;

    }


    /**
     * 登录状态检查服务
     */
    @Override
    public LoginCheckResultDTO checkLogin(LoginCheckDTO loginCheckDTO) {

        // 从header中获取token
        String token = loginCheckDTO.getRequest().getHeader("Authorization");

        LoginCheckResultDTO result = new LoginCheckResultDTO();

        try {
            // 根据token获取登录Id
            String loginId = String.valueOf(StpUtil.getLoginIdByToken(token));

            // token为空或id为空
            if (StringUtils.isAnyBlank(token, loginId)) {
                // 标记未登录。从定向至UserHub Login Page。
                result.setIsLogin(false);
                result.setRedirectUrl(buildUnauthenticatedUrl(
                        loginCheckDTO.getSubsystemLoginUrl(),
                        loginCheckDTO.getBack()));
                return result;
            }

            result.setIsLogin(true);
            result.setRedirectUrl(buildAuthenticatedUrl(
                    loginCheckDTO.getSubsystemLoginUrl(),
                    loginCheckDTO.getBack(),
                    token));
        } catch (Exception e) {
            log.error("登录状态检查异常", e);
            return null;
        }
        return result;
    }

    /**
     * 构建未认证重定向URL
     */
    private String buildUnauthenticatedUrl(String subsystemLoginUrl, String backUrl) {
        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(loginUrl);
        if (backUrl != null) {
            uriComponentsBuilder.queryParam(SUB_SYSTEM_BACK_URL, backUrl);
        }
        if (subsystemLoginUrl != null) {
            uriComponentsBuilder.queryParam(SUB_SYSTEM_LOGIN_URL, subsystemLoginUrl);
        }
        return uriComponentsBuilder.build().toUriString();
    }

    /**
     * 构建已认证重定向URL
     */
    private String buildAuthenticatedUrl(String subsystemLoginUrl, String backUrl, String token) throws UnsupportedEncodingException {

        // 如果没提供子系统登陆页面Url则默认重定向至UserHub首页
        if (subsystemLoginUrl == null) {
            return indexUrl;
        }

        // 构造Url
        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(subsystemLoginUrl);
        if (backUrl != null) {
            backUrl = URLEncoder.encode(backUrl, StandardCharsets.UTF_8.name());
            uriComponentsBuilder.queryParam(SUB_SYSTEM_BACK_URL, backUrl);
        }

        // 返回
        return uriComponentsBuilder
                .queryParam("tokenValue", token)
                .build().toUriString();
    }
}

